package edu.the.last.audio;

import java.io.ByteArrayInputStream;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

import edu.the.last.Messages;
import edu.the.last.graphic.MainFrame;
import edu.the.last.modell.Music;

/**
 * @author albertcsanis
 * 
 *         Write data to the OutputChannel.
 */
public class Playback implements Runnable {

	private SourceDataLine line;
	private Thread thread;
	private MainFrame parent;
	private final int bufSize = 16384;
	private Music music;

	/**
	 *  constructor
	 * 
	 * @param parent
	 *            the calling object, should be "this"
	 */
	public Playback(MainFrame parent, Music music) {
		this.parent = parent;
		this.music = music;
	}

	/**
	 * start the play back
	 */
	public void start() {
		thread = new Thread(this);
		thread.setName(Messages.getString("Playback.Playback")); //$NON-NLS-1$
		thread.start();
	}

	/**
	 * stop the play back
	 */
	public void stop() {
		thread = null;
	}

	/**
	 * stops the play back in case of an error
	 * 
	 * @param message
	 */
	private void shutDown(String message) {
		try {
			if (message != null) {
				throw new Exception(message);
			}
			if (thread != null) {
				thread = null;
				parent.enableButtons();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * the real play back
	 */
	public void run() {
		// make sure we have something to play
		if ( music.getAudioBytes()== null) {
			shutDown(Messages.getString("Playback.NotingtToPaly")); //$NON-NLS-1$
			return;
		}
		
		ByteArrayInputStream bais = new ByteArrayInputStream(music.getAudioBytes());
		AudioInputStream playbackInputStream = new AudioInputStream(bais, music.getFormat(), music.getAudioBytes().length / music.getFormat().getFrameSize());

		// define the required attributes for our line,
		// and make sure a compatible line is supported.

		DataLine.Info info = new DataLine.Info(SourceDataLine.class, music.getFormat());
		if (!AudioSystem.isLineSupported(info)) {
			shutDown(Messages.getString("Playback.Line") + info + Messages.getString("Playback.NotSuported")); //$NON-NLS-1$ //$NON-NLS-2$
			return;
		}

		// get and open the source data line for play back.

		try {
			line = (SourceDataLine) AudioSystem.getLine(info);
			line.open(music.getFormat(), bufSize);
		} catch (LineUnavailableException ex) {
			shutDown(Messages.getString("Playback.UnableToOpen") + ex); //$NON-NLS-1$
			return;
		}

		// play back the audio data

		int frameSizeInBytes = music.getFormat().getFrameSize();
		int bufferLengthInFrames = line.getBufferSize() / 8;
		int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
		byte[] data = new byte[bufferLengthInBytes];
		int numBytesRead = 0;

		// start the source data line
		line.start();

		while (thread != null) {
			try {
				if ((numBytesRead = playbackInputStream.read(data)) == -1) {
					break;
				}
				int numBytesRemaining = numBytesRead;
				while (numBytesRemaining > 0) {
					numBytesRemaining -= line.write(data, 0, numBytesRemaining);
				}
			} catch (Exception e) {
				shutDown(Messages.getString("Playback.Error") + e); //$NON-NLS-1$
				break;
			}
		}
		// we reached the end of the stream. play the data...
		if (thread != null) {
			line.drain();
		}
		line.stop();
		line.close();
		line = null;
		shutDown(null);
	}
}